perm filename 4RADD.DOC[P,JRA] blob sn#115471 filedate 1974-08-09 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00011 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002		This file holds new, untried sections for 4R.DOC.
C00003 00003	 CONTROL STRUCTURES  
C00010 00004		PROCEDURES
C00016 00005		CONSTANT VELOCITY MOTION
C00018 00006		TASK STRUCTURES
C00021 00007		LIBRARY ROUTINES
C00025 00008		IMPLEMENTATION OF ON-MONITORS
C00028 00009		INTERPRETER CODE FOR MOTIONS
C00030 00010		THE INTERPRETER SCHEDULER
C00036 00011	DEPROACHES
C00046 ENDMK
C⊗;
	This file holds new, untried sections for 4R.DOC.
The oontents are open for discussion.

 CONTROL STRUCTURES  
RHT has a later version of this.

	PROCESS SCHEDULING
	Of the three types of processes, joint servos need guaranteed
service,   and on-checkers  need frequent service.   Some on-checkers
listen to hardware  interrupts; these should  be at interrupt  level.
The interpreters may proceed at any pace.
	The mechanism  used is a  time-slot request list.   Each time
slot is one millisecond wide.  Joint servos and checkers reserve time
slots.   A single  time slot  may be reserved  by one  servo and  any
number of on-monitors.
	Different types of processes operate at different priorities:
	7. Reserved for future use.
	6. Clock, joint servo.
	5. Reserved for future use.
	4. Hardware and software on-monitors
	3. On-monitor bodies.
	2. Reseved for future use.
	1. Interpreters, scheduler (schedules interpreters).
	0. Background.

	The clock causes an interrupt every millisecond.  The level 6
interrupt handler examines the reservation list for the new slot, and
if there is any servo in that list (servo requests will come first in
the list, so this is a fast check),  the handler calls it.  The
servo  now  takes  control  and  executes  its  function.    When  it
retruns,   or  if there  was no  servo queued,  the clock-interrupt
handler causes  an interrupt to occur at level 4, and then dismisses.
The level 4 interrupt handler first checks to see  if it was awakened
due  to a  hardware interrupt.   If  so, it  branches to  the correct
on-monitor; if not, it  picks the first on-monitor  in the queue  for
this time slot and branches  to it.  When this monitor  returns,  the
interrupt  handler picks the  next in line,  and so on  until all are
gone, at  which  time  it  dismisses.   If  any  hardware  interrupts
occurred meanwhile, they now have a chance to be handled. The level 1
interrupt  handler picks the first interpreter  in line and starts it
going.
	When the clock next ticks,  it may interrupt a monitor  or an
interpreter.  In these  cases, their interrupts are left pending, and
upon exit from level 6, they will continue where they left off.
	PROCEDURES

	HAL  has  only   a  limited  capacity  for   procedures.  All
parameters  to a procedure  assume the planning  value "undefined" at
the conclusion of a procedure  call, except those which are  declared
as VALUE parameters  in the procedure heading, or those  stated to be
UNCHANGED  in the procedure  call. There is no  safeguard against the
accidental   modification  of  "unchanged"   parameters;   to   state
"unchanged" is entirely equivalent to an assertion that the parameter
has not changed its value during the execution of the procedure.  The
declaration of a procedure is this:
	 type  PROCEDURE name (argument list),
where  "type" is any datatype (and  is optional), and "argument list"
is a list of parameter names with their types.  An example:
	SCALAR  PROCEDURE  LGTH  (FRAME  F1,  F2;  VECTOR VALUE  V1);
This  declares that  LGTH is a procedure which returns  a scalar, and
takes as arguments two frames and one vector.  The vector is not
changed by the procedure.
	To call such a procedure:
	S1 ← LGTH(FROB, UNCHANGED HOLE, VECT);
This further asserts that HOLE is not changed by the call.
	Assertions are essential at the start of  a procedure body to
inform  the  compiler  of  the  values  to  expect  for  the  various
arguments. Remember that trajectories planned on the basis  of highly
inaccurate planning values will not work well.
	As a procedure is entered,  all variables have planning value
"undefined".   Globals may be accessed,  but they also have undefined
initial  planning  value.   All  variables  which  have  explicit  or
implicit  assignments to them  within a  procedure acquire  the value
"undefined" at the point directly after the procedure call.  In order
to  assert that  a variable  has  the same  value  as before,  ASSERT
(UNCHANGED F3).
	No modification  of the attach structure  is allowed inside a
procedure. The  compiler (often wrongly)  assumes that  there are  no
attachments involving variables within a procedure; to override this,
ASSERT (ATTACHED F1 F2).

	There  are four  special  types  of procedure  calls:  A  HAL
program might wish to call a routine coded for the PDP11 or a routine
coded for the  PDP10. Likewise, a  program on the  PDP10 may wish  to
control a HAL program, or a routine on the  PDP11 may wish to request
some arm motion.

	To  achieve the  first  two  cases,   there  exist  "external
procedures" in HAL.  These are compiled into calls on either routines
in the PDP11 or  routines in the runtime  PDP10 package.  To  declare
such a  procedure: EXTERNAL  PDP10 FRAME PROCEDURE  FOO (FRAME  A, B;
VECTOR V); This declares the procedure FOO to be a procedure resident
in the runtime PDP10 package,  expected to return a frame  value, and
taking as arguments two frames  and a vector. PDP10 procedures do not
have access  to the  actual variables  sent, since  copies are  made;
therefore, all  arguments to  PDP10 procedures  are considered to  be
VALUE parameters.
	It  is  possible to  declare  untyped  (i.e., statement-like,
instead of expression-like) procedures as well.  Replace "PDP10" with
"PDP11" for procedures in the  PDP11. PDP11 procedures do have access
to  values, and therefore parameters are not automatically considered
to be VALUE.

	To achieve  the  second two  cases,   there  exist  "internal
procedures"  in HAL.   It is  not necessary to  state from  where the
procedure may be  called.   Internal procedures  must be  at the  top
level of a HAL program.   The entire HAL program is  considered to be
an untyped procedure without parameters.
	CONSTANT VELOCITY MOTION

	To cause  the arm to  quickly achieve a  particular velocity,
and to hold it in straight-line motion for a given distance, there is
a special form of the MOVE instruction:
	MOVE YELLOW
		WITH VELOCITY=V1;
		THROUGH T;
		FOR DISTANCE=4;
		ENDM;
The WITH  VELOCITY tells which  vector to follow,  and how  fast. The
THROUGH T  tells the compiler where the move  expects to end. The FOR
DISTANCE tells the maximum distance the hand should go. It is assumed
that such a move will normally terminate by an ON test.
For example:

	MOVE YELLOW
		WITH VELOCITY=V1;
		THROUGH T;
		FOR DISTANCE=4;
		ON FORCE(V1) > 2 DO STOP;
		ENDM;
	TASK STRUCTURES

	An assembly task is often divided into  subtasks which form a
partial order with  respect to the order of execution.  An example is
a task A which contains four subtasks, B, C, D, and E, of which B and
C must  be done before D,  and D must be  done before E, but  B and C
could  be done  in any  order.  It  is possible  in HAL  to leave the
ordering of  the  subtasks up  to  the compiler,  which will  try  to
optimise the entire  operation.  The way to  specify the example just
given is as follows:
	TASKBEGIN "A"
		BEGIN "B"
		<code for task B>
		END "B";

		BEGIN "C"
		<code for task C>
		END "C";

		BEGIN "D AND E"
		PREREQUISITE "B", "C";
		<code for task D>
		<code for task E>
		END "D AND E"
	END "A"

	The word TASKBEGIN introduces a  "task block", which contains
a  set of statements.  These statements  may be  blocks identified by
strings and  may contain  the declaration  that  certain other  named
blocks within  the same  task are prerequisite  for the  inception of
this block.
	The order in which the statements are performed is determined
only insofar as the prerequisite conditions demand.  The compiler may
reorder them consistent with  the preconditions, and may even execute
some of the statements simultaneously  (as if there were a  COBEGIN),
if this is feasible.
	It is useful to place assertions at the beginning of the code
for any of the statements within a task; this assists the compiler in
maintaining its  world model, and  is also  used to  help decide  the
proper ordering of the tasks. It is likewise good to place assertions
at the end of each statement.
	LIBRARY ROUTINES

	There  is   a  library  of   useful  routines   which  system
programmers  have  prepared.   The  expander is  familiar  with these
routines, and  can  insert them  in  the  program wherever  the  user
wishes.  For  example, say there is a library  routine called INSERT,
which  needs to know screw-length and  hole-location. After the screw
has been picked up, the user can code:
	INSERT	(SCREW_LENGTH = 3, HOLE_LOCATION = F1);
	The  word INSERT  is  recognized as  a  special word  by  the
parser, which  has a table of currently  defined library routines and
the names of their arguments.
	Some library  routines  return values;  these values  may  be
obtained by an assignment statement:
	S1 ← COSINE (ANGLE = π/30);

	To create a library routine, this format is followed:
	MAKE_LIBRARY INSERT (SCALAR SCREW_LENGTH; FRAME HOLE_LOCATION);
		<code for insert>

	It is possible  to invoke the library routine  with arguments
arranged as for any other procedure call. In this case, the arguments
must correspond in order and  type with those in the statement  which
originally defined the  routine.  The advantage  of allowing symbolic
names  for the arguments is that the order  need not be recalled.  As
an example, the first call above could have been written:
	INSERT (3, F1);

	Yet another  syntax is acceptable  for invocation  of library
routines; it is included for compatibility with high level primitives
(see the section  on advanced language  features).   Here is how  the
INSERT call would look:
	INSERT
		WITH HOLE_LOCATION F1
		WITH SCREW_LENGTH 3;
The reserved word WITH  introduces a clause which specifies  a set of
parameters.   It is possible  to combine the two  parameters into one
clause, like this:
	INSERT
		WITH HOLE_LOCATION F1, SCREW_LENGTH 3;

	Some  library   routines  have   the  restriction  that   the
parameters are expected  to be names of variables, and not constants.
In the case that a constant  is supplied, a warning will be  printed,
and a temporary variable will be declared to hold the given constant.
	IMPLEMENTATION OF ON-MONITORS
This page is a set of notes for the eventual documentation.
Author:  RF

	The code for an on-monitor is in-line and jumped around.
The code is PDP11 code, including calls on arithmetic subroutines,
if neccessary.

Each on-monitor is given a unique name by the compiler.
The scheduler associates that name with the location of the
enable bit for this monitor.

On-monitors have only two states:  enabled and disabled.
There is a status word associated with each on-monitor;
it contains two bits:  the enabled, and the kill.

At the start of a program, all on-monitors are disabled.
That means enable=0, kill=0.

At the end of a block, the compiler includes code to disable all
on-monitors within that block.

Only one copy of an on-monitor ever exists.  No multiple instantiations.

ENABLE <NAME>
To enable a software monitor, 
tell kernel to activate it at its address, in on-level, where
it will have PDP11 code to:
1) look at its "enabled bit".  If already on, dismiss. If not, turn on.
2) clear all its working buffers (if any) 
3) schedule its second entry (the monitor itself) to be awakened at
the appropriate intervals.  Then dismiss.

The software monitor, every time it is awakened, does the following:
1)  look at its "kill bit".  If on, clear and dismiss.
2)  execute the code for its check.
3)  if condition satisfied, do immediate stuff,
 instantiate the conclusion, and dismiss.
4)  reschedule self at appropriate interval.  Dismiss.

To enable a hardware monitor,
tell kernel to activate it at its address, in on-level, where
it will have PDP11 code to:
1)  look at the device enabled bit.  If already on, return.  If not, turn on.
2)  dismiss.

DISABLE <NAME>
To disable a software monitor, set its  "kill bit".
To disable a hardware monitor, clear its "enabled bit".
	INTERPRETER CODE FOR MOTIONS
not yet ready.  Author:  RF.

The code generated for a move includes the following interpreter commands:

1) Prepare move.  This points to the move table, and causes the trajectory
to be modified to conform to current locations of via points.  Joint
loading is calculated for each via point as well.

2) Enable on-monitor.  There may be more than one of these instructions.

3) Start motion.  This points to the modified move table.

4) Await completion.  This causes the interpreter to wait until the
move has completed, or the arm has been stopped for some reason.
The signal which awakes the interpreter comes from whichever joint
servo finishes last: each servo, as it finishes, checks to see if all
the others are done, and if so, awakes the interpreter at servo level.

5) disable all on-monitors associated with move.  This is done at
servo level

6) dismiss to interpreter level.
	THE INTERPRETER SCHEDULER

	The interpreter scheduler is a set of routines used for
handling interpreter sprouting, termination, waiting, and scheduling,
and for on-monitor enabling and disabling.  The code "↑" means
switch to high priority (above any on-monitor); the code "↓" means
return to previous priority.

INTEGER ARRAY DEPENDENTS [0:MAXNAME];
	COMMENT:  Count of how many dependent interpreters each
	named interpreter has;

INTEGER ARRAY MOTHER [0:MAXNAME];
	COMMENT:  Each interpreter has a mother;

RCLASS PROCESSID (INTEGER NAME, PC; RPTR(PROCESSID) NEXT);
RPTR (PROCESSID) CURRENT;

RCLASS QUEUE (RPTR(PROCESSID) HEAD, TAIL);
RPTR(QUEUE) RUNQUEUE, WAITQUEUE;

PROCEDURE ENQUEUE(RPTR(QUEUE) WHERE; RPTR(PROCESSID) WHO);
	BEGIN
	COMMENT:  Puts a process on a queue.  QUEUE tells which
		queue.  WHO is the process id;
	QUEUE:TAIL(WHERE) ← PROCESSID:NEXT(QUEUE:TAIL(WHERE)) ← WHO;
	END;

RPTR(PROCESSID) PROCEDURE DEQUEUE (RPTR(QUEUE) WHERE);
	BEGIN
	COMMENT:  Returns head of queue, which is removed;
	RPTR(PROCESSID) ANSWER;
	ANSWER ← QUEUE:HEAD(WHERE);
	QUEUE:HEAD(WHERE) ← PROCESSID:NEXT(QUEUE:HEAD(WHERE));
	RETURN(ANSWER);
	END;

INTEGER PROCEDURE GENNAME;
	BEGIN
	COMMENT:  Assigns a new name  for a procedure from a heap of
	names.  All procedure names are integers;
	END;

PROCEDURE SPROUT (IPC);
	BEGIN
	COMMENT: IPC is the interpreter program counter.
	The effect is to put the new interpreter in the run-queue;
	INTEGER NEWNAME, MOTHERNAME;
	RPTR (PROCESSID) WHO;
	↑;
	NEWNAME ← GENNAME;
	WHO ← NEW_RECORD (PROCESSID);
	PROCESSID:NAME(WHO) ← NEWNAME;
	PROCESSID:PC(WHO) ← IPC;
	MOTHER(NEWNAME) ← MOTHERNAME ← PROCESSID:NAME(CURRENT);
	DEPENDENTS(MOTHERNAME) ← DEPENDENTS(MOTHERNAME) + 1;
	ENQUEUE(RUNQUEUE,WHO);
	↓;
	END;

PROCEDURE DISMISS;
	BEGIN  COMMENT:  Terminates the caller;
	INTEGER VICTIM;
	↑;
	VICTIM ← PROCESSID:NAME(CURRENT);
	IF DEPENDENTS(VICTIM) ≠ 0
	THEN 	BEGIN  COMMENT:  This should never happen;
		ERROR("ACTIVE PROCESS DEPENDS ON BLOCK BEING EXITED");
		JOIN(VICTIM);
		END;
	DEPENDENTS(MOTHER(VICTIM)) ← DEPENDENTS(MOTHER(VICTIM)) - 1;
	∀ ON | VICTIM⊗ON DO
		DISABLE(ON);
	CURRENT ← RNULL;
	↓;
	JRST RESCHEDULE;
	END;

PROCEDURE JOIN;
	BEGIN  COMMENT: Caller wishes to
	wait until all descendents have dismissed;
	↑;
	IF DEPENDENTS(PROCESSID:NAME(CURRENT)) ≠ 0
	THEN	BEGIN
		PROCESSID:PC(CURRENT) ← <RETURN ADDRESS OF NAME>;
		ENQUEUE(WAITQUEUE, CURRENT);
		↓;
		JRST RESCHEDULE;
		END
	ELSE ↓;
	END;

PROCEDURE ENABLE (OPC, OSW);
	BEGIN  COMMENT: OPC is on-monitor
	program counter, OSW is on-monitor status word;
	↑;
	MAKE NAME⊗OSW;
	PUSHJ (OPC);
	↓;

PROCEDURE DISABLE (OSW);
	BEGIN  COMMENT:  OSW is on-monitor status word;
	↑;
	KILL(OSW) ← 1;
	ENABLE(OSW) ← 0;
	↓;
	END;

PROCEDURE RESCHEDULE;
	BEGIN
	↑;
	IF CURRENT = RNULL 
	THEN 	BEGIN
		WHILE RUNQUEUE = RNULL DO BEGIN ↓; ↑; END;
		CURRENT ← DEQUEUE(RUNQUEUE);
		PUSH NAME(CURRENT);
		PUSH IPC(CURRENT);
		↓;
		PUSHJ INTERPRETER;
		END;
	ELSE	↓;
	END;

PROCEDURE SIGNAL (EVENT);
	BEGIN  COMMENT:  EVENT is a unique name for this event;
	∀ INTERPS | INTERPS⊗WAIT≡EVENT DO
		BEGIN
		DENY X⊗WAIT≡EVENT;
		IF ¬ X⊗WAIT≡ANY 
		THEN	BEGIN
			ENQUEUE (RUNQUEUE,<X, WITH ITS INFORMATION>);
			JRST RESCHEDULE;
			END;
		END;
	END;
DEPROACHES

	Many objects  have shapes which  necessitate care as  the arm
approaches  them or departs from  them.  HAL supplies  one method for
insuring that every  time the arm  approaches a  frame, it will  pass
through some  associated spot  first, and every  time it  leaves that
frame,   it passes once  again through the same spot.   The "spot" is
termed a DEPROACH  (from DEParture  and apPROACH),   and is really  a
transformation  to  be applied  to  the frame  involved  in order  to
discover the appropriate place through which to pass. The reason that
a transformation is used,  and not a frame itself, is that deproaches
are  often used  for large  objects, like the  table, and  the proper
point to pass through in that case is 10 centimeters above  where the
hand  is meant  to  arrive on  the  table (or  above  where the  hand
currently is  on the table,  if a departure is meant),  not the point
10 centimeters above the table origin.

	SPECIFICATION OF DEPROACHES
	One  specifies  the  deproach  of  an  object  by  making  an
assertion; for  example, the deproach  of the table  is automatically
asserted as follows:
	ASSERT FACT (DEPROACH TABLE [(0 0 10) | (0 0 0)]). This means
that the correct departure point  from a spot F on the  table will be
[(0 0 10) | (0 0 0)] * F.
	As an  object moves about in space,  its deproach point moves
as well. Thus ASSERT FACT (DEPROACH FROB [(0 10 0) | (0 0  0)]) means
that  no matter  where  the frob  may  go, its  deproach  will be  10
centimeters  in the y-direction away from  its origin, as measured in
its own coordinate system.
	What if  the hand  is not  at FROB,   but wishes  to use  its
deproach?  This also is handled correctly; the deproach point will be
10 centimeters in FROB's y-direction from wherever the hand  actually
is.
	Deproaches,  being  transformations,  also have the  power to
include  rotations. These  are considered to  be rotations  about the
origin of  the coordinate system  involved; the  rotation occurs,  as
usual, before translation.  Thus
	ASSERT FACT  (DEPROACH FROB  [(5 0  0) : (0  0 90)])  has the
effect  that whenever  FROB's deproach  is used  from any  frame, the
deproach frame will be  the given frame, rotated about  FROB's origin
by 90  degrees in Z,  and then translated  5 centimeters in  FROB's X
direction. The purpose  of including rotations  is for  completeness;
the user may seldom find them useful.
	The way deproaches are implemented is this:  Say that a frame
F  is given  deproach transformation D.   It  is desired to  find the
frame which  is  the deproach  point from  some  other frame  H  (for
example, where the hand is, for  departure), using F's deproach.  The
frame  which  is  used  is F  *  D  * (F→TABLE)  *  H.    This rather
complicated-looking expression has a particularly simple form  if H =
F, because of the identities (F → TABLE) * F = TABLE, and X * TABLE =
X; using these, we get that F's own deproach point is F * D.

	DEFAULT CHOICE OF DEPROACH
	In approaching a frame,  if that frame has a  deproach, it is
used.  If it does not, but is attached to some frame which does, then
that frame's deproach is used; if  that frame has no deproach,   then
the search continues up the ladder  of attachment until some frame is
found  with a deproach.   If none  at all is found,  then the table's
deproach is  used as a  default.   (One way  to think of  this is  to
consider all frames ultimately  attached to the table.) In aproaching
a frame which  is merely the  result of a  calculation (such as  MOVE
YELLOW TO FROB + (0 0 1)) the default deproach is null.
	In departing from a frame,  it  matters whether that frame is
now attached to the hand or not.  If not, then the same algorithm for
finding deproach is used as in the case of approach. But if the frame
has been detached  from some erstwhile mother and is  now attached to
the  hand,  then its  old mother's deproach is used  (and if there is
none, the same search is  made).  How can the compiler keep  track of
the fact that the frame was once attached to something else? Whenever
the statement "DETACH frob FROM mother" is encountered,  it generates
two assertions:
	DENY FACT (ATTACHED frob mother);
	ASSERT FACT (WAS_ATTACHED  frob mother).  Therefore,  a frame
attached to the hand still has some "memory" of its previous state of
attachment.

	EXPLICIT CHOICE OF DEPROACH
	All of  the automatic generation  of deproach  points can  be
explicitly overriden in a MOVE or  GO statement by means of the USING
clause.  This can take two forms:
	USING  APPROACH = NIL;  COMMENT: No approach point  at all is
used;
	USING APPROACH = DEPROACH(frob); COMMENT: frob's  deproach is
used;
	Note that the word  APPROACH could be replaced by DEPROACH in
each of the examples above.